package cooperate

import (
	"context"
	"fmt"
	"sync"
	"time"

	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/binocle/module/socket"
	"gddgame.cc/galaxy/common/feature/orbit/chess_protocol"
	"gddgame.cc/galaxy/common/namespaces/cooperate/structure"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/serialize"
	"github.com/gomodule/redigo/redis"
)

type LogicProxy interface {
	GetMap(mapNoId string) MapDelegate
	GetData(mapNoId string, t byte, id interface{}) PropsInstance
	ListData(mapNoId string, t byte, ids []interface{}) []PropsInstance
	ListDepend(mapNoId string, t byte, id interface{}, key string) []PropsInstance
	AddTask(mapNoId string, from []byte, task byte, time int64, content []byte, params []interface{})
	AddSchedule(mapNoId string, from []byte, target []byte, key string, schedule byte, time int64, b []byte, params []interface{}, delay int)
}

// 提供获取数据的外部接口
type LogicWrap struct {
	*Share
	MapDefined
	// 内部Operate可以获取到本地支持的mapInstance
	MapManager *MapManager

	logic *LogicExecute
	def.Logger
	def.Store
}

func NewLogicExecute(agent binocle.App, redis *redis.Pool) (*LogicWrap, error) {
	logic := &LogicExecute{
		//delay:        int64(delay) * 1000,
		agent: agent,
		Redis: redis,

		operateQueue:    utils.NewSafeNodeQueue(),
		operateChan:     make(chan struct{}, 30),
		operateBucket:   utils.NewThreaderBucket(agent, 10, 30),
		operateConn:     make([]structure.SafeConn, 10),
		taskChan:        make(chan *TaskBuilder, 30),
		taskGetChan:     make(chan chan []*structure.TaskInfo, 30),
		taskFinishChan:  make(chan string, 30),
		taskRestoreChan: make(chan string, 30),
		taskChanPool: sync.Pool{New: func() interface{} {
			return make(chan []*structure.TaskInfo)
		}},
		scheduleChan: make(chan *ScheduleBuilder, 30),
		schedulePool: utils.NewThreaderPool(agent, 30, 10),
	}
	logic.execute = structure.NewExecute(redis, agent.FullName()+"/cp", DateInt)
	logic.baseConnMutex = sync.Mutex{}
	logic.baseConn = logic.execute.Transaction()
	logic.baseConn.Connect()

	share := &Share{agent: agent, logic: logic}
	share.init()

	wrap := &LogicWrap{logic: logic, Share: share}

	return wrap, nil
}

func (wrap *LogicWrap) ID() string {
	return "logicWrap"
}

func (wrap *LogicWrap) SetMapManager(mapManager *MapManager) {
	wrap.MapManager = mapManager
	wrap.MapDefined = mapManager
	wrap.Share.mapManager = mapManager
	wrap.logic.MapDefined = mapManager
	// 设定缓存
	dataMap := make(map[structure.PublicDataType]*DataTypeContainer, len(wrap.logic.GetDataList()))
	for _, t := range mapManager.GetDataList() {
		dataMap[t] = NewDataTypeContainer()
	}
	wrap.logic.DataTypeMap = dataMap

	d := mapManager.DefaultInstance()
	wrap.ToMap(d)
}

func (wrap *LogicWrap) ToMap(mapInstance *MapInstance) {
	mapInstance.LogicExecute = wrap.logic
}

func (wrap *LogicWrap) Center() MapCenter {
	return wrap.logic
}

func (wrap *LogicWrap) Clear() error {
	conn := wrap.logic.execute.Transaction()
	keys := []string{}

	conn.Connect()
	wrap.logic.execute.TraverseAll(conn, wrap.logic.execute.Key, func(key string) {
		keys = append(keys, key)
	})
	fmt.Println("clear keys number", len(keys))
	for _, key := range keys {
		_ = conn.Send("DEL", key)
	}
	if err := conn.Commit(); err != nil {
		return err
	}
	if err := conn.Close(); err != nil {
		return err
	}

	// 清空缓存
	dataMap := make(map[structure.PublicDataType]*DataTypeContainer, len(wrap.logic.GetDataList()))
	for _, t := range wrap.logic.GetDataList() {
		dataMap[t] = NewDataTypeContainer()
	}
	wrap.logic.DataTypeMap = dataMap
	return nil
}

func (wrap *LogicWrap) BindType(td TypeDefinedManager) {
	wrap.logic.TypeDefinedManager = td
	wrap.logic.execute.SetDataType(wrap.logic.GetDataTypeMap())
	td.InitOperate(wrap.logic)
}

func (wrap *LogicWrap) BindService(service MapNodeService, delegate MapNodeDelegate) {
	wrap.Share.bindService(service)
	wrap.Share.bindDelegate(delegate)
	wrap.logic.Notice = wrap.Share
}

func (wrap *LogicWrap) BindCancel(ctx context.Context) {
	wrap.logic.ctx = ctx

	wrap.logic.inlineCtx, wrap.logic.inlineCancel = context.WithCancel(ctx)
}

func (wrap *LogicWrap) Stop() {
	wrap.logic.inlineCancel()
}

func (wrap *LogicWrap) OnStopMap(mapInstance *MapInstance) {
	fmt.Println("onstopmap")
	if !wrap.logic.needStop {
		// 停机时，链接自动关闭
		wrap.closeOrbit(mapInstance)
	}
	mapInstance.OnStop()
}

// 全局停止：
// 先设置停止标志，暂停：实时任务处理（不下发了），实时计划（不写入内存队列），分地图计划（只处理内存队列）
// 如果没有后续处理，处理完所有进程中数据：

// 全局开始：
// 启动操作处理队列 , 任务队列，计划调度
// 因为任务会有多地图数据：所以直接自动加载分地图（但不用初始化，可执行预加载逻辑）
// 分地图启动：分地图计划任务

func (wrap *LogicWrap) StartQueue() {
	// todo 提高性能，并行处理多个队列：按map和data划分
	go func() {
		for {
			select {
			case <-wrap.logic.ctx.Done():
				fmt.Println("needstop")
				wrap.logic.needStop = true
				return
			}
		}
	}()
	go func() {
		timer := time.NewTicker(100 * time.Millisecond)
		smallTimer := time.NewTicker(10 * time.Millisecond)
		openSmall := true

		op := func(operateContent *OperateContent, t int64, no int) {
			mapInstance := wrap.MapManager.GetMap(operateContent.MapNoId)
			if wrap.logic.checkList(mapInstance, operateContent.Checks) {
				if wrap.logic.operateConn[no] == nil {
					wrap.logic.operateConn[no] = wrap.logic.execute.Transaction()
				}
				conn := wrap.logic.operateConn[no]
				_ = wrap.logic.Operate(conn, mapInstance, operateContent.From, operateContent.Target, operateContent.Operates, operateContent.Message, int64(t)+wrap.logic.GetStartUp())
			}
		}
		popOp := func(tt int) bool {
			oc, t := wrap.logic.operateQueue.Top()
			if oc == nil {
				return false
			}
			if t > tt {
				return false
			}
			oc, t = wrap.logic.operateQueue.Pop()

			o := oc.(*OperateContent)
			wrap.logic.operateBucket.Thread(o.Key, func(no int) {
				op(oc.(*OperateContent), int64(t), no)
			})
			return true
		}
		//delay := wrap.logic.delay / 10
		for {
			select {
			case <-timer.C:
				tt := wrap.logic.CurrentTime()
				i := 0
				for ; i < 1000 && popOp(wrap.logic.FromStartUp(tt)); i++ {

				}
				if i == 1000 && !openSmall {

					//if wrap.logic.operateQueue.Len() > 0 && !openSmall {
					openSmall = true
					smallTimer = time.NewTicker(10 * time.Millisecond)
					//}
				} else if i == 0 {
					// 可以停止
					if wrap.logic.needStop {
						goto Return
					}
				}
			case <-smallTimer.C:
				tt := wrap.logic.CurrentTime()
				i := 0
				for ; i < 100 && popOp(wrap.logic.FromStartUp(tt)); i++ {

				}
				if i < 100 {
					smallTimer.Stop()
					openSmall = false
				}
			}
		}
	Return:
		timer.Stop()
		smallTimer.Stop()
		wrap.logic.inlineCancel()
	}()

	go func() {
		conn := wrap.logic.execute.Transaction()
		conn.Connect()
		for {
			select {
			case <-wrap.logic.inlineCtx.Done():
				goto Return
			case builder := <-wrap.logic.scheduleChan:
				conn.Begin()
				if !wrap.logic.needStop && builder.info.Time < wrap.logic.scheduleTime {
					//fmt.Println("add schedule", builder.info.Key)
					m := wrap.MapManager.GetMap(builder.info.MapNoId)
					if m != nil && m.IsStart {
						// 立即执行：只存info，不写入队列
						wrap.logic.execute.AddSchedule(conn, builder.info)
						m.ScheduleChan <- *builder.info
					} else {
						// 地图未启动，不执行
						builder.executor(conn)
					}
					//fmt.Println("add schedule end", builder.info.Key)
				} else {
					if wrap.logic.needStop {
						fmt.Println("schedule stop")
					}
					//fmt.Println("now schedule", builder.info.Key)
					builder.executor(conn)
				}
				if err := conn.Commit(); err != nil {
					fmt.Println("add schedule", err)
				}
			}
		}
	Return:
		_ = conn.Close()
	}()
	go func() {
		conn := wrap.logic.execute.Transaction()
		conn.Connect()
		for {
			select {
			case <-wrap.logic.inlineCtx.Done():
				goto Return
			case builder := <-wrap.logic.taskChan:
				//fmt.Println("task builder")
				conn.Begin()
				builder.executor(conn)
				_ = conn.Commit()
			case cc := <-wrap.logic.taskGetChan:
				conn.Begin()
				ti := wrap.logic.execute.PopTasks(conn, 20)
				_ = conn.Commit()
				cc <- ti
			case tid := <-wrap.logic.taskFinishChan:
				//fmt.Println("task finish")
				conn.Begin()
				wrap.logic.taskTime = wrap.logic.CurrentTime()
				wrap.logic.execute.FinishTask(conn, tid)
				_ = conn.Commit()
			case tid := <-wrap.logic.taskRestoreChan:
				//fmt.Println("task restore")
				conn.Begin()
				wrap.logic.execute.RestoreTask(conn, tid)
				_ = conn.Commit()
			}
		}
	Return:
		fmt.Println("stop queue")
		_ = conn.Close()
	}()
}

func (wrap *LogicWrap) Dispatch(mapInstance *MapInstance) {
	ctx := mapInstance.OnStart()
	ch := mapInstance.ScheduleChan
	closure := func(info structure.ScheduleInfo) {
		// 将延迟任务直接推进到当前时间
		if wrap.logic.scheduleAddition > 0 && info.Time < wrap.logic.scheduleAddition {
			info.Time += wrap.logic.scheduleDiff
		}
		if info.Time > wrap.logic.scheduleTime {
			wrap.logic.scheduleTime = info.Time
		}
		wrap.logic.schedulePool.Thread(func() {
			wrap.logic.AnlyzeSchedule(mapInstance, &info)
			info.Release()
		})
	}
	go func() {
		conn := wrap.logic.execute.Transaction()
		conn.Connect()
		timer := time.NewTicker(100 * time.Millisecond)
		smallTimer := time.NewTicker(time.Millisecond)
		openSmall := true
		for {
			select {
			case <-ctx.Done():
				goto Return
			case info := <-ch:
				closure(info)
			case <-timer.C:
				if wrap.logic.needStop {
					// 整体暂停，不抓取数据
					if openSmall {
						openSmall = false
						smallTimer.Stop()
					}
					continue
				}
				ll := wrap.logic.execute.PopSchedule(conn, mapInstance.MapNoId, wrap.logic.CurrentTime(), 1000)
				if ll == nil {
					continue
				}
				_ = conn.Commit()

				if wrap.logic.scheduleDiff == 0 {
					// 将延迟10秒的任务直接加速到当前时间
					if wrap.logic.CurrentTime()-ll[0].Time > int64(10*time.Second) {
						wrap.logic.scheduleAddition = wrap.logic.CurrentTime() - int64(60*time.Second)
						wrap.logic.scheduleDiff = wrap.logic.CurrentTime() - ll[0].Time
						fmt.Println("schedule addition", wrap.logic.scheduleAddition, wrap.logic.scheduleDiff)
					}
				}
				for _, info := range ll {
					closure(*info)
				}
				if !openSmall && len(ll) >= 500 {
					openSmall = true
					smallTimer = time.NewTicker(time.Millisecond)
				}
			case <-smallTimer.C:
				ll := wrap.logic.execute.PopSchedule(conn, mapInstance.MapNoId, wrap.logic.CurrentTime(), 1000)
				if ll == nil || len(ll) < 100 {
					openSmall = false
					smallTimer.Stop()
					continue
				}
				_ = conn.Commit()
				for _, info := range ll {
					closure(*info)
				}
			}
		}
	Return:
		fmt.Println("dispach", mapInstance.MapNoId)
		timer.Stop()
		if openSmall {
			smallTimer.Stop()
		}
		_ = conn.Close()
	}()
}

func (wrap *LogicWrap) ExistAccount(accountId int, openId string, identity int) {
	conn := wrap.logic.execute.Transaction()
	conn.Begin()

	data := structure.NewBaseData(AccountType, accountId)
	conn.Connect()
	// 获取所有
	info := wrap.logic.execute.Get(conn, data)
	if info == nil || len(info) == 0 {
		wrap.logic.clearDetailCache("", data)
		//id := wrap.logic.Execute.GetTypeId(conn, wrap.GetAccount())
		info = wrap.logic.GenerateAccount(accountId, openId, identity)
		wrap.logic.execute.Create(conn, data, info)
		//if id < accountId {
		//	fmt.Println("generate id", accountId)
		//	wrap.logic.Execute.ChangeTypeId(conn, wrap.GetAccount(), accountId)
		//}
		fmt.Println("no exist", accountId, info)
		if err := conn.Commit(); err != nil {
			fmt.Println("make account", err)
		}
	} else {
		fmt.Println("exist", accountId, info)
		identity = utils.ToInt(info[Identity])
	}
	if err := conn.Close(); err != nil {
		fmt.Println("make account", err)
	}
}

func (wrap *LogicWrap) MakeAccount(accountId int, openId string, identity int) int {
	data := structure.NewBaseData(AccountType, accountId)
	conn := wrap.logic.execute.Transaction()
	conn.Begin()
	//id := wrap.logic.Execute.IncrTypeId(conn, wrap.GetAccount())
	info := wrap.logic.GenerateAccount(accountId, openId, identity)
	wrap.logic.execute.Create(conn, data, info)
	if err := conn.Commit(); err != nil {
		fmt.Println("make account", err)
	}
	if err := conn.Close(); err != nil {
		fmt.Println("make account", err)
	}
	return accountId
}

func (wrap *LogicWrap) UpdateAccount(accountId int, data map[string]interface{}) {
	conn := wrap.logic.execute.Transaction()
	conn.Begin()
	wrap.logic.execute.Update(conn, structure.NewBaseData(AccountType, accountId), data)
	if err := conn.Commit(); err != nil {
		fmt.Println("make account", err)
	}
	if err := conn.Close(); err != nil {
		fmt.Println("make account", err)
	}
}

func (wrap *LogicWrap) Operate(mapNoId string, from []byte, target []byte, checkContent []byte, operateContent []byte, delay int64, message []byte) {
	slot := serialize.GetSlot()

	var checkList CheckDataSliceTwo
	if len(checkContent) > 0 {
		_ = slot.Set(&checkList)
		if err := slot.Unmarshal(checkContent); err != nil {
			fmt.Println("operate check", err)
		}
		slot.Clear()
	}
	var operateList OperateDataSlice
	if err := slot.Set(&operateList); err != nil {
		fmt.Println("operate", err)
	}
	if err := slot.Unmarshal(operateContent); err != nil {
		fmt.Println("operate", err)
	}
	slot.Clear()

	var operateMessage OperateMessage
	var om *OperateMessage
	if err := slot.Set(&operateMessage); err != nil {
		fmt.Println("operate message", err)
	}
	if err := slot.Unmarshal(message); err != nil {
		om = nil
	} else {
		om = &operateMessage
	}

	slot.Release()
	fromData := structure.NewBaseDataByte(from)
	targetData := structure.NewBaseDataByte(target)
	//fmt.Println("operate", fromData, targetData, delay, wrap.CurrentTime())
	wrap.logic.PushOperate(OperateContent{
		MapNoId:  mapNoId,
		From:     fromData,
		Target:   targetData,
		Checks:   checkList,
		Operates: operateList,
		Message:  om,
	}, delay)
}

func (wrap *LogicWrap) AddSchedule(mapNoId string, from []byte, target []byte, key string, schedule byte, time int64, b []byte, params []interface{}, delay int) {
	scheduleLogic := structure.ScheduleLogic(schedule)

	fromData := structure.NewBaseDataByte(from)
	targetData := structure.NewBaseDataByte(target)
	dd := wrap.logic.FormatTime(time)

	builder := wrap.logic.ScheduleBuilder()
	builder.BindMapNoId(mapNoId).Target(*targetData).From(fromData)
	builder.Key(key).Bind(scheduleLogic, b).Schedule(dd, params, delay)
}

//
//func (wrap *LogicWrap) BindEvent(mapNoId string, from []byte, target []byte, event byte, mode byte, content []byte) {
//	conn := wrap.logic.execute.Transaction()
//	conn.Begin()
//
//	//fromData := structure.NewBaseDataByte(from)
//	targetData := structure.NewBaseDataByte(target)
//	wrap.logic.BindEvent(conn, mapNoId, targetData, event, structure.EventMode(mode), content)
//	if err := conn.Commit(); err != nil {
//		fmt.Println("bind event", err)
//	}
//	if err := conn.Close(); err != nil {
//		fmt.Println("bind event", err)
//	}
//}

func (wrap *LogicWrap) GetTasks(number int) []*structure.TaskInfo {
	if wrap.logic.needStop {
		return nil
	}
	tt := wrap.logic.taskChanPool.Get().(chan []*structure.TaskInfo)
	wrap.logic.taskGetChan <- tt
	ti := <-tt
	wrap.logic.taskChanPool.Put(tt)
	return ti
}

func (wrap *LogicWrap) FinishTask(tid string) {
	wrap.logic.taskFinishChan <- tid
}

func (wrap *LogicWrap) RestoreTask(tid string) {
	wrap.logic.taskRestoreChan <- tid
}

func (wrap *LogicWrap) AddTask(mapNoId string, from []byte, task byte, time int64, content []byte, params []interface{}, callback *TaskCallback) {
	builder := wrap.logic.TaskBuilder()
	builder.From(structure.NewBaseDataByte(from))
	builder.BindMapNoId(mapNoId).Bind(structure.TaskEvent(task), content).Task(time, params)
}

func (wrap *LogicWrap) StatValues(data PropsInstance, statConfigs []utils.StatConfig) map[utils.CycleKey]map[string]int32 {
	conn := wrap.logic.execute.Transaction()
	conn.Begin()
	values := data.Stat().Values(wrap.logic.execute, conn, data, statConfigs)
	if err := conn.Close(); err != nil {
		fmt.Println("add task", err)
	}
	return values
}

func (wrap *LogicWrap) closeOrbit(mapInstance MapProxy) {
	// 发送给所有
	if err := wrap.logic.agent.Request("orbit").Handler(func(response socket.Response, err error) {
		fmt.Println(response, err)
	}).Do("chess/close", &chess_protocol.CloseData{MapNoId: mapInstance.GetMapNoId(), Mode: mapInstance.GetMode()}); err != nil {
		fmt.Println("orbit chess close", err)
	}
}
